#ifndef xpack_test_helper
#define xpack_test_helper
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::test_helper::inc
#undef  private
#undef  protected
#define private     public
#define protected   public
#include"define/base_type.hpp"
#include"macro/xstruct.hpp"
#include"macro/private/log.hpp"
#include"macro/xdefer.hpp"
#include"macro/xexport.hpp"
#include"macro/xinit.hpp"
#include"macro/xlink.hpp"
#include"macro/xmaybe_unused.hpp"
#include"macro/xmodify.hpp"
#include"macro/xthread_local.hpp"
#include"macro/xwarning.hpp"
#pragma pop_macro("xuser")

xwarning_disable(4390)

/*
用法：

// 这个遵循包含范式的命名方式（和模块目录结构对应）
// 测试模块放到 test/ 目录
#ifndef xpack_test_path_to_your_module
//                 ^^^^^^^^^^^^^^^^^^^ 修改

#define xpack_test_path_to_your_module
//                 ^^^^^^^^^^^^^^^^^^^ 修改

#pragma push_macro("xuser")
#undef  xuser

// 定义 xuser
#define xuser mixc::test_path_to_your_module::inc
//                       ^^^^^^^^^^^^^^^^^^^ 修改

// 包含单元测试工具
#include"test/helper.hpp"

// 包含待测模块
#include"path/to/your/module.hpp"

// 对比的实现（可选）
#include"xxxx.h"

#pragma pop_macro("xuser")

namespace mixc::test_path_to_your_module{
    //               ^^^^^^^^^^^^^^^^^^^ 修改

    xtest("模块名称"){
    //     ^^^^^^^^ 修改

        type_expect     expect;     // 期望值
        type_actually   actually;   // 实际值

        expect          = expect_func(xthe same input...);
        actually        = my_func(xthe same input...); // 已有参考对象的情况下

        // 如果期望它们相等
        xassert_eq(expect, actually);

        ...
        ...
        ...

        // 假如 actually 为数值类型
        // 已只结果的情况下，可以直接使用常量代替 expect
        xassert_ne(1111, actually);

        ...
        ...
        ...

        // 在一个循环里
        for(uxx i = 0; i < 100; i++){
            uxx j = ...;
            ...
            ...
            ...

            xassert_gt(expect, actually, i, j); // <- 如果断言失败，还可以打印相关信息 i、j 的值（参数个数不限制）

            ...
            ...
            ...

            xassert_lt(expect, actually){
                // 如果断言失败就走此处的逻辑
                ...
                ...
                ...

                // 此处不是闭包，return 会直接退出此测试单元
                // return
            }
        }
    };
}

#endif
*/

namespace mixc::test_helper{
    struct foo{};

    struct mixc_test_context{
        uxx     case_count = 0;
        uxx     pass_count = 0;
        uxx     line;
        uxx     error_limit;
        asciis  path;
        asciis  func_name;

        mixc_test_context(asciis path, uxx line, asciis func_name, uxx error_limit = 64);
        ~mixc_test_context();

        template<class ... args_t>
        bool fail_if(bool fail, uxx line, asciis message, args_t const & ... rest){
            using namespace inc;

            if (case_count++; not fail){
                pass_count++;
                return false;
            }
            if (case_count == pass_count + 1){
                log(log_type_t::for_test, path, line, func_name, "[FAIL]", message_type_t::fail);
            }

            log(log_type_t::for_fail, path, line, func_name, message, message_type_t::fail, rest...);

            if (--error_limit == 0){
                throw foo{};
            }
            return true;
        }
    };

    template<uxx priority_v = inc::the_test>
    struct test_item : inc::init_listx<priority_v>{
        template<class lambda_t>
        struct wrap{
            static void invoke(){
                try{
                    lambda_t()();
                }
                catch(foo){}
            }
        };

        template<class lambda_t>
        test_item(lambda_t const &):
            inc::init_listx<priority_v>(xmodify(wrap<lambda_t>::invoke)){
        }
    };

    inline f64 __mixc_assert_abs(f64 value){
        return value < 0 ? -value : value;
    }

    xthread_local(__mixc_assert_epsilon, f64) = xthread_local_new(1e-10);
    xthread_local(__mixc_test_context, mixc_test_context *);
}

#define xtest(FUNC_NAME,...)                                                                        \
       ::mixc::test_helper::test_item<__VA_ARGS__> xlink2(__test, __COUNTER__) =                    \
    /*这里要使用 __COUNTER__，如果用 __LINE__ 通过 xgen. 生成 xtest() 框架代码时会有重复的符号*/    \
    [](xmaybe_unused                                                                                \
       ::mixc::test_helper::mixc_test_context && __test_context =                                   \
       ::mixc::test_helper::mixc_test_context(__FILE__, __LINE__, FUNC_NAME, ## __VA_ARGS__))

#define __xassert(CONDITION,...)    if ((*::mixc::test_helper::__mixc_test_context)->fail_if(not (CONDITION), __LINE__, #__VA_ARGS__, ## __VA_ARGS__))
#define __xassert_cmp(OP,LEFT,RIGHT,...)                                                                                                            \
    if (                                                                                                                                            \
        (                                                                                                                                           \
            [&](auto const & __left, auto const & __right){                                                                                         \
                return (*::mixc::test_helper::__mixc_test_context)->fail_if(                                                                        \
                    not (__left OP __right), __LINE__, #LEFT ", " #RIGHT ", " #__VA_ARGS__, __left, __right, ## __VA_ARGS__                         \
                );                                                                                                                                  \
            }                                                                                                                                       \
        )(LEFT, RIGHT)                                                                                                                              \
    )


#define xassert(CONDITION,...)                                                                                                                      \
    if (                                                                                                                                            \
        (                                                                                                                                           \
            [&](auto const & __condition){                                                                                                          \
                return (*::mixc::test_helper::__mixc_test_context)->fail_if(                                                                        \
                    not __condition, __LINE__, #CONDITION ", " #__VA_ARGS__, __condition, ## __VA_ARGS__                                            \
                );                                                                                                                                  \
            }                                                                                                                                       \
        )(CONDITION)                                                                                                                                \
    )


// 期望 left == right
#define xassert_eq(LEFT,RIGHT,...)  __xassert_cmp(==, LEFT, RIGHT, ## __VA_ARGS__)

// 期望 left != right
#define xassert_ne(LEFT,RIGHT,...)  __xassert_cmp(!=, LEFT, RIGHT, ## __VA_ARGS__)

// 期望 left >= right
#define xassert_ge(LEFT,RIGHT,...)  __xassert_cmp(>=, LEFT, RIGHT, ## __VA_ARGS__)

// 期望 left >  right
#define xassert_gt(LEFT,RIGHT,...)  __xassert_cmp(> , LEFT, RIGHT, ## __VA_ARGS__)

// 期望 left <  right
#define xassert_lt(LEFT,RIGHT,...)  __xassert_cmp(< , LEFT, RIGHT, ## __VA_ARGS__)

// 期望 left <= right
#define xassert_le(LEFT,RIGHT,...)  __xassert_cmp(<=, LEFT, RIGHT, ## __VA_ARGS__)

// 测试浮点近似相等
#define __xassert_fe(CONDITION,...) if (__test_context.fail_if(not CONDITION, __LINE__, #__VA_ARGS__ ",", ## __VA_ARGS__))

// 允许指定精度内近似相等
#define xassert_fe(LEFT,RIGHT,...)  __xassert_fe(                                               \
    (                                                                                           \
        ([&](auto const & l, auto const & r){                                                   \
            using namespace mixc::test_helper;                                                  \
            return                                                                              \
                (l == r/*无穷大的情况下方比较不适用*/) ? true :                                 \
                (l) == 0 ? __mixc_assert_abs(r) <= *__mixc_assert_epsilon :                     \
                (r) == 0 ? __mixc_assert_abs(l) <= *__mixc_assert_epsilon :                     \
                (__mixc_assert_abs((l - r) / l) <= *__mixc_assert_epsilon);                     \
        })(LEFT, RIGHT)                                                                         \
    ), LEFT, RIGHT, ## __VA_ARGS__)

// 设置全局 xassert_fe 比较精度
#define xassert_epsilson_global(...)        *::mixc::test_helper::__mixc_assert_epsilon = __VA_ARGS__

// 设置局部 xassert_fe 比较精度
#define xassert_epsilson_scoped(...)                                                            \
    auto xlink(__backup_mixc_assert_epsilon_, __LINE__) =                                       \
        *::mixc::test_helper::__mixc_assert_epsilon;                                            \
    xdefer{                                                                                     \
        *::mixc::test_helper::__mixc_assert_epsilon =                                           \
            xlink(__backup_mixc_assert_epsilon_, __LINE__);                                     \
    };                                                                                          \
    xassert_epsilson_global(__VA_ARGS__)


#define xassert_nlrd(N,L,R,D)                                       \
    {                                                               \
        using namespace ::mixc::test_helper::origin::helper;        \
        xassert_eq(nlrd_counter::c_normal, uxx(N));                 \
        xassert_eq(nlrd_counter::c_left_ref, uxx(L));               \
        xassert_eq(nlrd_counter::c_right_ref, uxx(R));              \
        xassert_eq(nlrd_counter::d_counter, uxx(D));                \
        nlrd_counter::reset_counter_for_cd();                       \
    }

#define xassert_alr(L,R)                                            \
    {                                                               \
        using namespace ::mixc::test_helper::origin::helper;        \
        xassert_eq(nlrd_counter::a_left_ref, uxx(L));               \
        xassert_eq(nlrd_counter::a_right_ref, uxx(R));              \
        nlrd_counter::reset_counter_for_assign();                   \
    }

namespace mixc::test_helper::origin::helper{
    xstruct(
        xname(nlrd_counter)
    )
        nlrd_counter(uxx value = 0): 
            value(value){
            c_normal++;
        }

        nlrd_counter(nlrd_counter & other): 
            value(other.value){
            c_left_ref++;
        }

        nlrd_counter(nlrd_counter && other): 
            value(other.value){
            c_right_ref++;
        }

        auto & operator=(nlrd_counter & other){
            value = other.value;
            a_left_ref++;
            return *this;
        }

        auto & operator=(nlrd_counter && other){
            value = other.value;
            a_right_ref++;
            return *this;
        }

        uxx value                   = 0;

        static void reset_counter(){
            reset_counter_for_assign();
            reset_counter_for_cd();
        }

        static void reset_counter_for_cd(){
            c_normal      = 0;
            c_left_ref    = 0;
            c_right_ref   = 0;
            d_counter     = 0;
        }

        static void reset_counter_for_assign(){
            a_left_ref    = 0;
            a_right_ref   = 0;
        }

        static inline uxx c_normal      = 0;
        static inline uxx c_left_ref    = 0;
        static inline uxx c_right_ref   = 0;
        static inline uxx a_left_ref    = 0;
        static inline uxx a_right_ref   = 0;
        static inline uxx d_counter     = 0;

        ~nlrd_counter(){
            d_counter++;
        }
    $
}

#endif

#include"mixc.hpp"

xexport_space(mixc::test_helper::origin)